{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "048876d8-c05f-45fd-883c-f756aee5cfc4",
   "metadata": {},
   "source": [
    "# Lesson 5: Conversational Question answering"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad214456-a809-4f50-8d4a-7a418c8e47f1",
   "metadata": {},
   "source": [
    "![](./images/rag_diagram.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f7fa1b1-cb6d-4d47-951b-292d976bfcc2",
   "metadata": {},
   "source": [
    "# Retrieval chain from previous lesson"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51038d2e-40fa-4b27-9f38-b85fea191264",
   "metadata": {},
   "source": [
    "Let's split and load our documents into a vector store and create a retriever. Then we will convert its output to a string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74876e19-8ff4-4fa2-b372-667fec90aa4a",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": [
    "import \"dotenv/config\";"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c961bb82-f3b7-4905-95ed-d3aee3c2474a",
   "metadata": {
    "height": 114
   },
   "outputs": [],
   "source": [
    "import { loadAndSplitChunks } from \"./lib/helpers.ts\";\n",
    "\n",
    "const splitDocs = await loadAndSplitChunks({\n",
    "    chunkSize: 1536,\n",
    "    chunkOverlap: 128\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3353866-3f0a-4c07-9d4f-b660135ceb76",
   "metadata": {
    "height": 114
   },
   "outputs": [],
   "source": [
    "import { initializeVectorstoreWithDocuments } from \"./lib/helpers.ts\";\n",
    "\n",
    "const vectorstore = await initializeVectorstoreWithDocuments({\n",
    "  documents: splitDocs,\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7cdc91b8-29ac-47f3-a97a-95f3985c51c2",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": [
    "const retriever = vectorstore.asRetriever();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86bc1ecb-bb66-463d-a249-2b4892c79c13",
   "metadata": {
    "height": 267
   },
   "outputs": [],
   "source": [
    "import { RunnableSequence } from \"@langchain/core/runnables\";\n",
    "import { Document } from \"@langchain/core/documents\";\n",
    "\n",
    "const convertDocsToString = (documents: Document[]): string => {\n",
    "  return documents.map((document) => {\n",
    "    return `<doc>\\n${document.pageContent}\\n</doc>`\n",
    "  }).join(\"\\n\");\n",
    "};\n",
    "\n",
    "const documentRetrievalChain = RunnableSequence.from([\n",
    "    (input) => input.question,\n",
    "    retriever,\n",
    "    convertDocsToString\n",
    "]);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bd51824-6f2d-4d1d-a636-de1738a2232a",
   "metadata": {},
   "source": [
    "now that we have a retriever, lets build a retriever chain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ef4bd14-afcc-4708-b89f-c1c892d17237",
   "metadata": {
    "height": 386
   },
   "outputs": [],
   "source": [
    "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n",
    "\n",
    "const TEMPLATE_STRING = `You are an experienced researcher, \n",
    "expert at interpreting and answering questions based on provided sources.\n",
    "Using the provided context, answer the user's question \n",
    "to the best of your ability using only the resources provided. \n",
    "Be verbose!\n",
    "\n",
    "<context>\n",
    "\n",
    "{context}\n",
    "\n",
    "</context>\n",
    "\n",
    "Now, answer this question using the above context:\n",
    "\n",
    "{question}`;\n",
    "\n",
    "const answerGenerationPrompt = ChatPromptTemplate.fromTemplate(\n",
    "    TEMPLATE_STRING\n",
    ");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07faae94-4724-4dac-a85f-521cc1dc1bb9",
   "metadata": {
    "height": 131
   },
   "outputs": [],
   "source": [
    "import { ChatOpenAI } from \"@langchain/openai\";\n",
    "import { StringOutputParser } from \"@langchain/core/output_parsers\";\n",
    "\n",
    "const model = new ChatOpenAI({\n",
    "    modelName: \"gpt-3.5-turbo-1106\"\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7397cbcb-56be-4833-83b2-c5e92b0e3a19",
   "metadata": {
    "height": 165
   },
   "outputs": [],
   "source": [
    "const retrievalChain = RunnableSequence.from([\n",
    "  {\n",
    "    context: documentRetrievalChain,\n",
    "    question: (input) => input.question,\n",
    "  },\n",
    "  answerGenerationPrompt,\n",
    "  model,\n",
    "  new StringOutputParser(),\n",
    "]);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09645f74-86ab-4c2e-b50b-78e7886798e4",
   "metadata": {},
   "source": [
    "# Adding history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d678f1d-c37c-462f-96ba-d1c84ab35482",
   "metadata": {
    "height": 267
   },
   "outputs": [],
   "source": [
    "import { MessagesPlaceholder } from \"@langchain/core/prompts\";\n",
    "\n",
    "const REPHRASE_QUESTION_SYSTEM_TEMPLATE = \n",
    "  `Given the following conversation and a follow up question, \n",
    "rephrase the follow up question to be a standalone question.`;\n",
    "\n",
    "const rephraseQuestionChainPrompt = ChatPromptTemplate.fromMessages([\n",
    "  [\"system\", REPHRASE_QUESTION_SYSTEM_TEMPLATE],\n",
    "  new MessagesPlaceholder(\"history\"),\n",
    "  [\n",
    "    \"human\", \n",
    "    \"Rephrase the following question as a standalone question:\\n{question}\"\n",
    "  ],\n",
    "]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18f6254b-5eaf-4867-999b-97d08f96cee9",
   "metadata": {
    "height": 114
   },
   "outputs": [],
   "source": [
    "const rephraseQuestionChain = RunnableSequence.from([\n",
    "      rephraseQuestionChainPrompt,\n",
    "      new ChatOpenAI({ temperature: 0.1, modelName: \"gpt-3.5-turbo-1106\" }),\n",
    "      new StringOutputParser(),\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c326a7cc-d9be-432c-be10-7846d2b8e5e9",
   "metadata": {
    "height": 182
   },
   "outputs": [],
   "source": [
    "import { HumanMessage, AIMessage } from \"@langchain/core/messages\";\n",
    "\n",
    "const originalQuestion = \"What are the prerequisites for this course?\";\n",
    "\n",
    "const originalAnswer = await retrievalChain.invoke({\n",
    "  question: originalQuestion\n",
    "});\n",
    "\n",
    "console.log(originalAnswer);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b25e21c-28d0-4329-852e-b96a14c38529",
   "metadata": {
    "height": 165
   },
   "outputs": [],
   "source": [
    "const chatHistory = [\n",
    "  new HumanMessage(originalQuestion),\n",
    "  new AIMessage(originalAnswer),\n",
    "];\n",
    "\n",
    "await rephraseQuestionChain.invoke({\n",
    "  question: \"Can you list them in bullet point form?\",\n",
    "  history: chatHistory,\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c76381b8-b08f-4244-af09-e4bc836f9c88",
   "metadata": {},
   "source": [
    "# Putting it all together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c325c8b5-9292-41f2-ad10-3ad0ad3df182",
   "metadata": {
    "height": 182
   },
   "outputs": [],
   "source": [
    "const convertDocsToString = (documents: Document[]): string => {\n",
    "  return documents.map((document) => `<doc>\\n${document.pageContent}\\n</doc>`).join(\"\\n\");\n",
    "};\n",
    "\n",
    "const documentRetrievalChain = RunnableSequence.from([\n",
    "  (input) => input.standalone_question,\n",
    "  retriever,\n",
    "  convertDocsToString,\n",
    "]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5fe1d4c-9075-47d4-8451-b9bbc1115668",
   "metadata": {
    "height": 352
   },
   "outputs": [],
   "source": [
    "const ANSWER_CHAIN_SYSTEM_TEMPLATE = `You are an experienced researcher, \n",
    "expert at interpreting and answering questions based on provided sources.\n",
    "Using the below provided context and chat history, \n",
    "answer the user's question to the best of \n",
    "your ability \n",
    "using only the resources provided. Be verbose!\n",
    "\n",
    "<context>\n",
    "{context}\n",
    "</context>`;\n",
    "\n",
    "const answerGenerationChainPrompt = ChatPromptTemplate.fromMessages([\n",
    "  [\"system\", ANSWER_CHAIN_SYSTEM_TEMPLATE],\n",
    "  new MessagesPlaceholder(\"history\"),\n",
    "  [\n",
    "    \"human\", \n",
    "    \"Now, answer this question using the previous context and chat history:\\n{standalone_question}\"\n",
    "  ]\n",
    "]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9cb3853d-826c-4e1f-a413-9f9f0faf62fe",
   "metadata": {
    "height": 182
   },
   "outputs": [],
   "source": [
    "import { HumanMessage, AIMessage } from \"@langchain/core/messages\";\n",
    "await answerGenerationChainPrompt.formatMessages({\n",
    "  context: \"fake retrieved content\",\n",
    "  standalone_question: \"Why is the sky blue?\",\n",
    "  history: [\n",
    "    new HumanMessage(\"How are you?\"),\n",
    "    new AIMessage(\"Fine, thank you!\")\n",
    "  ]\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3dd60bd2-fcc1-4f96-8763-969e30d52c13",
   "metadata": {
    "height": 250
   },
   "outputs": [],
   "source": [
    "import { RunnablePassthrough } from \"@langchain/core/runnables\";\n",
    "\n",
    "const conversationalRetrievalChain = RunnableSequence.from([\n",
    "  RunnablePassthrough.assign({\n",
    "    standalone_question: rephraseQuestionChain,\n",
    "  }),\n",
    "  RunnablePassthrough.assign({\n",
    "    context: documentRetrievalChain,\n",
    "  }),\n",
    "  answerGenerationChainPrompt,\n",
    "  new ChatOpenAI({ modelName: \"gpt-3.5-turbo\" }),\n",
    "  new StringOutputParser(),\n",
    "]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8f81c9f-88d7-470a-b597-c4137c9d35a4",
   "metadata": {
    "height": 63
   },
   "outputs": [],
   "source": [
    "import { RunnableWithMessageHistory } from \"@langchain/core/runnables\";\n",
    "import { ChatMessageHistory } from \"langchain/stores/message/in_memory\";"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3b2e1ba-0419-4276-bdb4-3b044b3a7f46",
   "metadata": {
    "height": 148
   },
   "outputs": [],
   "source": [
    "const messageHistory = new ChatMessageHistory();\n",
    "\n",
    "const finalRetrievalChain = new RunnableWithMessageHistory({\n",
    "  runnable: conversationalRetrievalChain,\n",
    "  getMessageHistory: (_sessionId) => messageHistory,\n",
    "  historyMessagesKey: \"history\",\n",
    "  inputMessagesKey: \"question\",\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24bc88b0-851c-4304-a085-ac0cba9f615f",
   "metadata": {
    "height": 284
   },
   "outputs": [],
   "source": [
    "const originalQuestion = \"What are the prerequisites for this course?\";\n",
    "\n",
    "const originalAnswer = await finalRetrievalChain.invoke({\n",
    "  question: originalQuestion,\n",
    "}, {\n",
    "  configurable: { sessionId: \"test\" }\n",
    "});\n",
    "\n",
    "const finalResult = await finalRetrievalChain.invoke({\n",
    "  question: \"Can you list them in bullet point form?\",\n",
    "}, {\n",
    "  configurable: { sessionId: \"test\" }\n",
    "});\n",
    "\n",
    "console.log(finalResult);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2133bd0-0e4d-4985-a62a-ee1a47fcba9e",
   "metadata": {},
   "source": [
    "https://smith.langchain.com/public/fca11abd-c0ec-456f-800e-6edfaf6bcf68/r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ac36205-de1c-4cfb-81e8-9c9d1525b338",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b547772d-4f1f-48e8-b381-138b30d993af",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac25b85d-dd40-42a1-8374-6c778e86c9d8",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7af107da-cf4c-4900-a456-d131ff8fbc80",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbcaa28f-5057-4109-80c1-406ec47bd9eb",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "464b5f7d-75e0-4366-8adf-48053f19229d",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e50feff9-72d1-4c1a-96b9-ec80ccde87f9",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "258343e5-e488-4c2c-938b-8c3189ef959d",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ec20e0c-389f-4cd1-b5df-67f6d706fb4e",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4b2b76f-f963-4226-95f1-42d15487f0ca",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99a497d1-0587-431b-bc54-657e935af6df",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67198cbc-9731-490a-85d2-4db5c8169735",
   "metadata": {
    "height": 29
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Deno",
   "language": "typescript",
   "name": "deno"
  },
  "language_info": {
   "file_extension": ".ts",
   "mimetype": "text/x.typescript",
   "name": "typescript",
   "nb_converter": "script",
   "pygments_lexer": "typescript",
   "version": "5.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
